package wanju_gameframe

import (
	cfg "gamesdk/wanyu/server/api/gameapi/gamecfg"

	"github.com/golang/protobuf/proto"

	b3core "github.com/magicsea/behavior3go/core"
)

/*
	本文件声明gameapi的所有接口
	框架层接口：Room、player、RobotMgr，这三个类属于框架层，所有接口均已实现，实现层可直接调用
	实现层接口：GameFactory、Game、Robot，这三个类属于实现层，所有接口由具体的游戏负责实现，框架层将调用实现层的接口

	框架类描述：
		player：管理玩家信息，例如id号、昵称、余额等玩家信息，此外还担负玩家在游戏中实时扣费的操作，玩家基本信息在创建Player的时候就已经初始化，可放心大胆地调用，一个玩家（包括真实玩家和机器人）对应一个Player实例
		Room：游戏房间，一个房间对应一桌游戏，可以将一个房间直接理解为一张游戏桌子，room是gameapi最重要的类，每个Room实例都内含一个协程用以处理房间内游戏事件（例如玩家进入/退出，游戏下注、碰牌等游戏事件）
		RobotMgr：机器人管理，每个Room内含一个RobotMgr实例，RobotMgr管理本房间内的所有机器人，每个RobotMgr内含一个协程用于负载机器人相关的运算

	关于线程安全：
		gameapi采用无锁多线程（多协程）架构模式必须考虑线程安全，本文档内除了特殊标注线程安全的接口之外，其余接口都为非线程安全，调用的时候请注意，下面列出接口调用的线程模式
		实现层接口：
			Robot类的所有接口由RobotMgr内的协程调用
			GameFactory、Game类的所有接口由Room内的协程调用
		框架层接口：
			框架层所有数据由框架层的协程维护，如果实现层开辟单独的协程来调用框架层的接口会导致线程安全问题，所有实现层不能有单独开协程直调框架层接口的情况
*/

/*
	通用型玩家事件处理
	本类所有接口由各游戏类型负责具体实现
*/
type PlayerAction interface {
	//玩家进入游戏，返回true表示允许玩家进入，否则表示拒绝进入，isNewPlayer为false表示暂时离开后马上又回来的玩家（未将该玩家从玩家列表中移除），如果isNewPlayer为false，OnPlayerSitDown的返回值无意义
	OnPlayerSitDown(player Player, isNewPlayer bool) bool

	//玩家离开，返回true表示允许玩家离开，否则表示不允许离开，如果返回true，无论游戏处于什么状态，上层都会将该玩家从玩家列表中移除
	OnPlayerStandUp(player Player) bool

	//主要用于交互式启动游戏的模式，例如玩家按了界面上“准备好了”按钮，该函数会被触发，目前暂时没啥用
	OnPlayerReady(playerId string) bool
}

/*
	定时器接口类
	本类提供定时器添加、撤销、一次性延迟任务等定时器功能，目的是框架层统一管理定时器，以后方便统一性调整，所有定时器的执行都会调用game接口的Ontimeout函数
	本类所有接口均已实现，game层可直接调用
	本类基于具体业务，仅服务于游戏运行期，所有接口只有在游戏处于运行状态时才可以调用，当一局游戏结束，框架层会自动撤销所有定时任务和一次性任务
*/

type UserData struct {
	//调用者的对象指针
	Obj interface{}
	//绑定的其他数据
	UserData interface{}
}
type Timer interface {
	//添加一个定时器
	//输入：userData：UserData对象，frequency：执行周期，单位毫秒
	//输出：返回值大于等于0表示该定时器的id，可用于后续定时器的撤销，返回值小于0表示添加失败
	AddTimer(userData UserData, frequency int) int

	//添加一个延迟执行任务，本任务只会执行一次
	//输入：userData：UserData对象，after：延迟时间，单位毫秒
	//输出：返回值大于等于0表示该定时器的id，可用于后续定时器的撤销，返回值小于0表示添加失败
	OnceTimer(userData UserData, after int) int

	//根据定时器id移除指定的定时任务，因游戏结束时框架层会自动撤销所有定时任务，所以一般只适用于中途撤销的应用场景，不推荐使用
	RemoveTimer(id int)
}

/*
	游戏工厂接口，每种游戏对应一个实现该接口的实例
	本类所有接口由各游戏类型负责具体实现
	本类所有接口如果调用失败请panic一个错误到上层，上层统一处理，所有接口如果返回则表示一定成功
*/

type RobotCfg struct {
	//游戏配置
	GameCfg *cfg.GameCfg
	//游戏场次配置
	KindCfg *cfg.KindCfg
	//游戏扩展配置
	ExGameCfg interface{}
	//游戏场次扩展配置
	ExKindCfg interface{}
}

type GameFactory interface {
	//分配游戏扩展配置buf，第一个参数为游戏级别扩展配置，第二个参数为游戏子类型级别扩展配置，两个返回值必须是结构体指针类型
	CreateExCfgBuf() (interface{}, interface{})


	//创建行为树，返回一颗树
	CreateBehaviorTree() *b3core.BehaviorTree

	//创建一个机器人，r是上层room接口
	CreateRobot(mgr RobotMgr, player Player, tree *b3core.BehaviorTree, robotCfg *RobotCfg) Robot
}

/*
	游戏接口，每个游戏房间对应一个实现该接口的实例
	本类所有接口由各游戏类型负责具体实现
	本类所有接口如果调用失败请panic一个错误到上层，上层统一处理，所有接口如果返回则表示一定成功
*/

//玩家游戏数据
type PlayerLogData struct {
	//玩家id
	PlayerId string
	//牌桌费
	TableScore int64
	//开局时玩家的余额(扣除牌桌费后的值)
	Score int64
	//输赢
	WinScore int64
	//结束时玩家的余额
	EndScore int64
	//税收
	Tax int64
	//暗税
	InvisibleTax int64
	//结果数据，如炸金花中牌的类型，斗牛中的牌型
	Result interface{}
	//私有数据 所有的牌
	Data interface{}
	//杀放概率
	KillRate float32
	//控制类型
	CtType CtrlType
	// 是否真的受杀放概率控制，本字段只对真实玩家有意义
	IsCtrl bool
}


/*
	玩家接口，每个玩家（包含机器人）都对应一个实现该接口的实例
	本类所有接口由框架层实现，各游戏只管调用即可
	本类所有接口如果调用失败请panic一个错误到上层，上层统一处理，所有接口如果返回则表示一定成功
*/

//玩家静态信息
type PlayerAttri struct {
	//玩家id
	PlayerID string
	//玩家所属代理的代理id
	AgentID string
	//玩家账号id，该id在所属代理商唯一，但在整个api平台不唯一
	AccountID string
	//头像id
	AvatartID int
	//昵称，目前没啥用
	NickName string
	//是否是机器人
	IsRobot bool

	AvatarURL string

	Gender    int32

	// 角色皮肤
	RoleSkin  string
}

//玩家动态信息
type PlayerInfo struct {
	//玩家ip所属区域，例如香港、北京。。。，如果是机器人，本值是通过生成随机ip然后查表得到的
	Region string
	//玩家ip，如果是机器人，本值随机生成
	Ip string
	//玩家坐席
	Seat int
	//是否是机器人
	IsRobot bool
	//是否暂时离开
	IsLeave bool

}

//本结构体内所有钱的单位都是元*10的6次方
type PlayerFinance struct {
	//余额
	Balance int64
	//流水（该字段对机器人无意义）
	ScoreFlow int64
	//当局游戏产生的流水（该字段对机器人无意义）
	TempScoreFlow int64
	//本局输赢
	RoundWinLose int64
	//本局体验场输赢
	RoundWinLoseExperience int64
	//本局明税
	RoundVisibleTax int64
	//本局暗税
	RoundInvisibleTax int64
	//本局一次暗税
	RoundOnceInvisibleTax int64
	//本局牌桌费
	RoundTableServerCharge int64
	//额外控制输赢值
	ExtraControlWinLose int64
	//总上分（该字段对机器人无意义）
	Credit int64
	//总下分（该字段对机器人无意义）
	Debit int64
	//明税（该字段对机器人无意义）
	VisibleTax int64
	//暗税（该字段对机器人无意义）
	InvisibleTax int64
}

//玩家控制信息
type PlayerCtrl struct {
	//是否特控
	IsSpecialCtrl bool
	//控制策略
	CtrlStrategy string
	//控制进度
	CtrlProccess string
}

type CtrlType int

const (
	//不受控（以机器人为杀放手段的游戏, 当关闭机器人时此枚举值生效）
	CtDefault CtrlType = 0
	//管控
	CtMgr CtrlType = 1
	//特控
	CtSpecial CtrlType = 2
	//个人水位控
	CtPersonPool CtrlType = 3
	//系统水位控
	CtSysPool CtrlType = 4
)

type ScoreResultType int

const(

	WinLosePP ScoreResultType = 0	//玩家和玩家的输赢

	WinLosePR ScoreResultType = 1	//玩家和机器人的输赢

	WinLoseRR ScoreResultType = 2 	//机器人和机器人的输赢
)

type ScoreResult struct {

	Score    int64     //含税输赢

	ScoreNotTax int64  //不含税输赢
}

type Player interface {

	//体验场加钱或减钱
	AddMoneyExperience(winLose int64)

	//加钱或减钱，money为正则加钱，反之则减钱，投注类游戏会立即调用支付服务，对战类游戏在游戏结算时再调支付服务,winLose 为不含税输赢，加减的值都是税前的，如果是非游戏行为加减钱，则赋值0
	AddMoney(money, winLose int64)

	//累计玩家流水，只对捕鱼、红黑大战、多福多财有效
	AddScoreFlow(scoreFlow int64)

	//获取玩家静态属性
	GetAttri() *PlayerAttri


	//获取玩家动态信息
	GetInfo() *PlayerInfo

	//获取玩家财务相关信息（不返回指针）
	GetFinance() PlayerFinance

	//获取玩家控制信息
	GetCtrl() PlayerCtrl

	//获取该玩家的杀放概率
	//返回值：
	//	第一个值：返回值为[-1, 1]区间的浮点数，为负代表系统针对玩家放水的概率，为正代表系统针对玩家杀分的概率，0表示不杀不放
	//	第二个值：受控类型，受控类型
	GetKillRate() (float32, CtrlType)

	//累加明税，目前暂时没用，以后可能会用到
	AddVisibleTax(tax int64)

	//累加暗税，只对捕鱼、多福多财有意义
	AddInvisibleTax(tax int64)

	//增加牌桌费
	AddTableServerCharge(serverCharge int64)

	//增加额外控制输赢值
 	AddExtraControlWinLose(controlWinLose int64)

	//设置控制进度
	SetControlProccess(proccess string, notice bool)

	//设置点控
	SetPersonControl(level int32,targetVal int64)

	//计算目标值与进度值的溢出值
	CalOverflowTgFh() int64

	//根据传入值计算目标值与进度值的溢出值
	CalOverflowTgFhCurrent(winLose int64) int64

	//已经完成特殊控制
	FinishCtrl()

	//获取被跟踪玩家的id
	GetTargetPlayer() string

	// 获取玩家日志前缀，打调试日志时使用，此接口会自动调用room.go的GetDLogPrefix()并拼接，相当于调用此接口，房间维度的日志信息也会自动获取
	GetDLogPrefix() string

	//游戏结束
	OnGameOverLog(playerLogData *PlayerLogData)

	//提交金币变动
	CommitMoney() error
}

/*
	房间相关接口
	本类所有接口由框架层实现，各游戏只管调用即可
	原则上讲，房间本来就是通用层，不应该存在接口的概念，这里引入接口主要是为了解除环形依赖
	大部分接口如果调用失败将抛出一个panic（不抛panic的接口请看具体注释说明）
*/
type Room interface {

	Experience() bool

	//定时器
	Timer
	GameOver() bool
	//写业务日志
	Log(player Player, optType int32, optData interface{})

	//通过玩家id获取玩家
	GetPlayer(playerId string) Player

	//通过坐席号获取玩家
	GetPlayerBySeat(seatId int) Player

	//获取房间内玩家个数，本接口无视玩家是否是观战状态、是否已输牌
	GetPlayerCount() int

	//以坐席递增为序，获取指定玩家id的后一个玩家，本接口无视玩家是否是观战状态、是否已输牌
	GetNextPlayer(player Player) Player

	//以坐席递增为序，获取指定玩家id的前一个玩家，本接口无视玩家是否是观战状态、是否已输牌
	GetPrevPlayer(player Player) Player


	//获取游戏配置，第一个返回值是游戏通用配置，第二个参数是游戏子类型通用配置，第三个参数是游戏扩展配置，第四个参数是游戏子类型扩展配置
	//当roomsrv通知gameapi游戏配置信息有变动时，gameapi的gamecfg包会立即解析配置信息并校验数据合法性，所以，本函数不会做任何配置信息解析相关的运算
	//当game层通知框架层游戏就绪通知时，框架层会向gamecfg获取一次最新的配置信息
	//建议game层不要保存该函数返回的配置数据，最好即用即调， 一是没必要，二是当roomsrv通知gameapi游戏配置有变动时，game层不会得到通知
	GetGameCfg() (*cfg.GameCfg, *cfg.KindCfg, interface{}, interface{})


	//遍历所有玩家并执行一个玩家的回调函数
	//回调函数返回true表示需要继续遍历下一个节点，否则终止遍历，接口返回true表示遍历完了所有节点，否则表示未遍历完所有节点
	//注意：回调函数中请不要对玩家列表做增加节点、删除节点、修改节点前驱、后继指针指向的操作，否则将导致未定义行为
	EachPlayer(fn func(Player) bool) bool

	//获取当前游戏局号，只有游戏在gaming状态才能被调用，否则将触发一个panic
	GetRoundId() string

	//发送私人信息，如果目标是机器人则直接将消息路由到robot进行处理
	SendPersonEvent(player Player, eventType int32, event proto.Message)

	//发送广播信息，如果目标是机器人则直接将消息路由到robot进行处理，每个机器人都将获得一份独立的消息
	SendBroadCastEvent(eventType int32, event proto.Message)

	//发送信息给观察者，上帝视角
	SendEvent2God(eventType int32, event proto.Message)

	//获取当前游戏房间号
	GetRoomID() string

	//代理商id
	GetAgentID() string

	//数据持久化，被持久化的结构体的定义必须首字母大写
	PersistedData(key string, v interface{})

	//读取已持久化的数据，如果读取失败将会抛出panic，读取成功但没有key或key对应的值为空时不会抛panic
	GetPersistentData(key string) []byte

	//让陪玩代替机器人进入房间玩
	ReplaceByFollower() bool

	//推送游戏公告信息，例如xx玩家在多福多财中单词旋转赢了100w时需要调用本接口推送游戏公告，此接口只供捕鱼和多福多财调用，其他游戏框架层可以处理
	SendBulletinInfo(msg string)

	//获取游戏公告产生的阀值
	GetBulletinThreshold() int

	//移除玩家，目前暂时只支持移除机器人
	RemovePlayer(player Player)

	// 获取room维度的调试日志前缀
	GetDLogPrefix() string

	//获得场次等级
	GetMatchLevel() uint32

	//获得控制手牌
	GetControlCard() (result map[string][]uint32, isConfig bool)

	//更新控制数据
	UpdatePlayerControl(players ...Player)

	//设置房间是否禁止匹配
	SetIsForbidMatch(isForbidMatch bool)

	//改变输赢明细(如:玩家与机器人 或玩家与玩家的输赢记录)targetIsRobot:目标对象是否是机器人 score:含税(为正则加钱，反之则减钱) scoreNotTax(为正则加钱，反之则减钱) 不含税
	ChangeScoreResult(player Player,targetIsRobot bool, score ,scoreNotTax int64)

	//收取房间服务费
	TakeTableServerCharge()
}

/*
	机器人类，每个机器人对应一个该类实例
	本类所有接口由各游戏类型负责具体实现
*/
type Robot interface {
	//来自游戏框架层的消息
	OnFrameMsg(eventType int32, event proto.Message)

	//处理具体游戏事件
	OnGameEvent(eventType int32, event proto.Message)

	//获取导出给决策树的导出数据，此接口必须返回结构体的指针
	GetExportData() interface{}

	//定时器，当游戏处于运行状态时，框架层会每一段时间调用一次该接口
	//输入： timeId:定时器id, userData:设置定时器时传给框架层的参数
	//输出：shouldContinue: 定时器是否继续，如果返回false，框架层将自动撤销该定时器
	OnTimeout(timerID int, userData interface{}) (shouldContinue bool)
}

/*
	机器人管理类，每个游戏房间对应一个该类实例，其内部会开辟一个独立的线程（协程）用于机器人的事件处理及AI运算
	本类所有接口由框架层实现，各游戏只管调用即可
*/
type RobotMgr interface {
	//定时器
	Timer

	//获取行为树
	GetBehaviorTree() *b3core.BehaviorTree
}

